﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Animation;

namespace Emilie.Animation
{
    /// <summary>
    /// Provides base helper classes for animation techniques
    /// </summary>
    public static class Base
    {
        internal static Random r = new Random();
        internal static List<StoryboardStorage> Storage = new List<StoryboardStorage>();

        public enum TranslateAxis
        {
            X,
            Y
        }

        internal static String GenerateUniqueID()
        {
            return r.NextDouble().ToString() + r.NextDouble().ToString();
        }

      

        /// <summary>
        /// Sets the composite transform origins for a Framework Element. If no Composite Transform exists, one will be created.
        /// </summary>
        /// <param name="_target">Target FrameworkElement</param>
        /// <param name="X">Centre X value (0.5 is middle)</param>
        /// <param name="Y">Centre Y value (0.5 is middle)</param>
        public static void SetCompositeTransformCentre(FrameworkElement _target, double X, double Y)
        {
            var ct = _target.GetCompositeTransform();
            ct.CenterX = X;
            ct.CenterY = Y;
            _target.RenderTransformOrigin = new Point(X, Y);
        }

        /// <summary>
        /// Applies storyboard options to the given storyboard
        /// Ignore this nonsense.
        /// </summary>
        /// <param name="_sb">Storyboard to modify</param>
        /// <param name="_options">Options to apply</param>
        internal static void ApplyStoryboardOptions(Storyboard _sb, StoryboardOptions _options)
        {
            _sb.ApplyStoryboardOptions(_options);
        }

        /// <summary>
        /// Removes all active storyboards registered in the given FrameworkElement's resource dictionary.
        /// </summary>
        /// <param name="_target">FrameworkElement to remove storyboards from</param>
        public static void RemoveLocalStoryboards(FrameworkElement _target)
        {
            _target.ClearLocalStoryboards();
        }

        internal static void sb_Completed(object sender, object e)
        {
            var s = sender as Storyboard;
            s.Completed -= sb_Completed;
            Base.Unregister(s);
        }

        /// <summary>
        /// Register Storyboard with animation memory manager
        /// </summary>
        /// <param name="_target">The element who's resources contains the storyboard</param>
        /// <param name="_sb">The storyboard to register</param>
        /// <param name="_key">The string Key ID of the resource</param>
        public static void Register(FrameworkElement _target, Storyboard _sb, String _key)
        {
            var s = new StoryboardStorage
            {
                target = _target,
                sb = _sb,
                Key = _key
            };

            Storage.Add(s);
            _sb.Completed += Base.sb_Completed;
        }

        public static String RegisterToWindow(this FrameworkElement target, Storyboard storyboard, String partialKey)
        {
            var id = storyboard.AddToWindowResources(partialKey);

            var s = new StoryboardStorage
            {
                target = target,
                sb = storyboard,
                Key = id
            };

            Storage.Add(s);
            storyboard.Completed += Base.sb_Completed;

            return id;
        }

        /// <summary>
        /// Experiments looking at memory usage. Don't use externally.
        /// </summary>
        /// <param name="in_story"></param>
        internal static void Unregister(Storyboard in_story)
        {
            // Find our storage item
            StoryboardStorage s = null;
            foreach (var st in Storage.Where(st => st.sb == in_story)) s = st;

            // Clean it up!
            if (s != null)
            {
                var resources = s.target.Resources;
                object Key = (from r in resources where r.Key.ToString() == s.Key select r.Key).FirstOrDefault();
                if (Key != null) s.target.Resources.Remove(Key);
            }
            
            in_story = null;
            s.sb = null;
            if (s != null) Storage.Remove(s);
            s.target = null;
            s.Key = null;
            s = null;
            //GC.Collect();
        }
    }

}
